/*
 * @Author: yangzonglong
 * @Date: 2021-02-23 14:30:48
 * @version: v1.0.0
 * @Descripttion:
 * @LastEditors: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git
 * @LastEditTime: 2023-11-26 21:17:26
 * @Auditor:
 */
import React, { useRef, useCallback, memo } from 'react';
import { useDrag, DragSourceMonitor, useDrop, DropTargetMonitor } from 'react-dnd';
import { XYCoord } from 'dnd-core';
import { ControlType, controlMap, dragSourceMap } from './../../../../config';
import { ControlProps, DragSourceProps, DragDirectionProps } from './../../../../interface';

interface DragItemProps {
  type: string;
  index: number;
  data: ControlProps;
}

interface CanvasItemProps {
  item: ControlProps;
  index: number;
  setActiveControl: (control: ControlProps) => void;
  moveControl(id: string, toId: string, type?: string): void;
  setDragReducer(data: Record<string, any>): void;
  dragDirection?: DragDirectionProps;
  dragSource?: DragSourceProps;
  hoverControlId?: string;
  children?: React.ReactNode;
  curControl?: ControlProps;
  dropAccept: string[]
}

let moveToId = '',
  hoverTimer: number | null = null,
  hoverRowTimer: number | null = null;

const createBaseDropAccept = (parentId?: string) => {
  const values = Object.values(ControlType);
  const arr: string[] = [];

  values.forEach(item => {
    arr.push((parentId || '') + item);
    arr.push('add_' + item);
  });

  return values;
};

let isDrop = false;

export default memo(({
  item,
  setActiveControl,
  children,
  index,
  moveControl,
  dragDirection,
  hoverControlId,
  dragSource,
  setDragReducer,
  curControl,
  dropAccept
}: CanvasItemProps) => {
  const ref = useRef<HTMLDivElement | null>(null);
  const ReactControl: any = controlMap[item.type];

  const onClick = useCallback(
    (e: React.BaseSyntheticEvent) => {
      window._ACTIVE_ID_ = item.id;
      setActiveControl(item);
      e.stopPropagation();
    },
    [setActiveControl, item]
  );
  
  const [, drag] = useDrag({
    type: item.id,
    
    item: () => {
      moveToId = '';
      isDrop = false;
      setDragReducer({ dragSource: 'canvas' }); // 清空拖动信息
      return item
    },

    collect: (moniter: DragSourceMonitor) => ({
      isDragging: moniter.isDragging(), // dragCollect.isDragging
    }),

    end() {
      setDragReducer({
        hoverControlId: '',
        dragDirection: '',
        dragSource: ''
      });

      if (item.id && moveToId) {
        if(isDrop) {
          isDrop = false;
          return
        }
        moveControl(item.id, moveToId);
      }
    },
  });

  function hover(dragItem: DragItemProps, monitor: DropTargetMonitor) {
    const dragIndex = dragItem.index,
      hoverIndex = index;

    if (item.id === curControl?.id) return;
    if (hoverTimer) clearTimeout(hoverTimer);
    
    hoverTimer = window.setTimeout(() => {
      let dragDirection = '';

      const hoverBoundingRect = ref.current?.getBoundingClientRect(); // hover dom
      const clientOffset = monitor.getClientOffset();
      const diffClientOffset = monitor.getDifferenceFromInitialOffset();

      if (!clientOffset || !diffClientOffset || !hoverBoundingRect) return;

      if (Math.abs(diffClientOffset.x) < Math.abs(diffClientOffset.y)) {
        // 上下移动
        const hoverClientY = (clientOffset as XYCoord).y - hoverBoundingRect.top;
        const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;

        if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) return;
        if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) return;

        if (hoverClientY > hoverMiddleY) dragDirection = 'down';
        else dragDirection = 'up';
      } else {
        // 左右移动
        const hoverClientX = (clientOffset as XYCoord).x - hoverBoundingRect.left;
        const hoverMiddleX = (hoverBoundingRect.right - hoverBoundingRect.left) / 2;

        if (dragIndex < hoverIndex && hoverClientX < hoverMiddleX) return;
        if (dragIndex > hoverIndex && hoverClientX > hoverMiddleX) return;

        if (hoverClientX > hoverMiddleX) dragDirection = 'right';
        else dragDirection = 'left';
      }

      hoverTimer = null;
      moveToId = item.id;
      hoverRowTimer = null;
      setDragReducer({ dragDirection, hoverControlId: item.id });
    }, 5);
  }

  const [dropCollect, drop] = useDrop({
    accept: dropAccept,

    collect: (monitor: DropTargetMonitor) => ({
      isOver: monitor.isOver({ shallow: true }),
      canDrop: monitor.canDrop(),
    }),

    drop: (dropItem: any, monitor: DropTargetMonitor) => {
      if (monitor.didDrop()) {
        // 发生在嵌套目标上
        let dropResult: any = monitor.getDropResult()
        return { data: dropResult.data };
      }

      isDrop = true;
      moveControl(dropItem.data?.id || dropItem.type, item.id, 'drop');
      return { data: item };
    },

    hover(dragItem: any, monitor: DropTargetMonitor) {
      if (!ref.current) return;
      hover(dragItem, monitor);
    },
  });

  const domComplete = useCallback(
    (dom: HTMLDivElement) => {
      ref.current = dom;
      drag(drop(ref));
    },
    [drag, drop]
  );

  return (
    <ReactControl
      {...item.options}
      scriptsChangeData={{}}
      isDesign
      onClick={onClick}
      id={item.id}
      domComplete={domComplete}
      data-candrop={dropCollect.canDrop && dropCollect.isOver}
      data-controlid={item.id}
      data-controltype={item.type}
      data-hover={item.id === hoverControlId && dragSource === dragSourceMap.CANVAS}
      data-direction={dragDirection}
      data-active={curControl?.id === item.id}
    >
      {children}
    </ReactControl>
  );
}
);
